﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using log4net;
using TeamCommunitySolution.Modules.Leagues;
using TeamCommunitySolution.Modules.Metrics.Requests;
using TeamCommunitySolution.Modules.Metrics.Responses;
using TeamCommunitySolution.Modules.Shared;
using TeamCommunitySolution.Repositories.Metrics;
using TeamCommunitySolution.Domain.DataLayer.PlayerMetrics;

namespace TeamCommunitySolution.Modules.Metrics
{
    public class MetricsManagerImpl : MetricsManager
    {
        /// <summary>
        /// Logger instance.
        /// </summary>
        private static readonly ILog _log = LogManager.GetLogger(typeof(MetricsManagerImpl));

        /// <summary>
        /// Base Repository reference.
        /// </summary>
        private MetricsRepository metricsRepo;

        /// <summary>
        /// Prevents a default instance of the <see cref="MetricsManagerImpl"/> class from being created. 
        /// </summary>
        private MetricsManagerImpl()
        {
        }

        /// <summary>
        /// Creates the instance.
        /// </summary>
        /// <param name="metricsRepository">The metrics repository.</param>
        /// <returns>Metrics Manager Impl</returns>
        public static MetricsManager CreateInstance(MetricsRepository metricsRepository)
        {
            MetricsManagerImpl output = new MetricsManagerImpl();
            output.metricsRepo = metricsRepository;

            return output;
        }

        /// <summary>
        /// Creates the group.
        /// </summary>
        /// <param name="createRequest">The create request.</param>
        /// <returns>Metric Group Response</returns>
        public MetricGroupResponse CreateGroup(MetricGroupCreateRequest createRequest)
        {
            if (createRequest == null)
            {
                throw new InvalidMetricGroupCreateRequest("The request cannot be null.");
            }

            if (string.IsNullOrEmpty(createRequest.GroupName))
            {
                throw new InvalidMetricGroupCreateRequest("The metric group create request requires a name.");
            }

            MetricGroupResponse response = new MetricGroupResponse();

            try
            {
                MetricGroup group = this.metricsRepo.GetMetricGroupByField("Name", createRequest.GroupName);

                if (group == null)
                {
                    group = new MetricGroup();
                    group.Name = createRequest.GroupName;
                    this.metricsRepo.Save(ref group);
                }

                response.MetricGroup = group;
                response.Status = OperationStatus.Success;
            }
            catch (MetricsRepositoryException e)
            {
                response.Status = OperationStatus.Failure;
                response.Message = string.Format("Incomplete metric group creation for request {0} : {1}", createRequest.GroupName, e.Message);
                response.Exception = e;
                _log.Error(response.Message, e);
            }

            return response;
        }

        /// <summary>
        /// Creates the definition.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <returns>Metric Definition Response</returns>
        public MetricDefinitionResponse CreateDefinition(MetricDefinitionCreateRequest request)
        {
            if (request == null)
            {
                throw new InvalidMetricDefinitionCreateRequest("The request cannot be null");
            }

            if(String.IsNullOrEmpty(request.DefinitionName))
            {
                throw new InvalidMetricDefinitionCreateRequest("The definition name cannot be empty in the create request.");
            }

            if(request.Group == null)
            {
                throw new InvalidMetricDefinitionCreateRequest("The definition requires a metric group");
            }

            if (MetricsRepositoryImpl.CreateInstance().GetMetricGroupByField("Id", request.Group.Id) == null)
            {
                throw new InvalidMetricDefinitionCreateRequest("The definition requires a valid metric group");
            }

            MetricDefinitionResponse response = new MetricDefinitionResponse();

            try
            {
                MetricDefinition definition = this.metricsRepo.GetMetricDefinitionByField("Name", request.DefinitionName);
                
                if (definition == null)
                {
                    definition = new MetricDefinition();
                    definition.Name = request.DefinitionName;
                    definition.Group = request.Group;
                    this.metricsRepo.Save(ref definition);
                }

                response.MetricDefinition = definition;
                response.Status = OperationStatus.Success;
            }
            catch (MetricsRepositoryException e)
            {
                response.Status = OperationStatus.Failure;
                response.Message = string.Format("Incomplete metric definition creation for request {0} : {1}", request.DefinitionName, e.Message);
                response.Exception = e;
                _log.Error(response.Message, e);
            }

            return response;
        }

        /// <summary>
        /// Gets the metric group.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <returns>Metric Group Response</returns>
        public MetricGroupResponse GetMetricGroup(MetricGroupRequest request)
        {
            MetricGroupResponse response = new MetricGroupResponse();

            try
            {
                response.MetricGroup = this.metricsRepo.GetMetricGroupByField("Id", request.Id);
                response.Status = OperationStatus.Success;
            }
            catch (MetricsRepositoryException e)
            {
                response.Status = OperationStatus.Failure;
                response.Message = string.Format("Incomplete metric group lookup for request {0} : {1}", request.Id, e.Message);
                response.Exception = e;
                _log.Error(response.Message, e);
            }

            return response;
        }

        /// <summary>
        /// Gets the metric definition.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <returns>Metric Definition Response</returns>
        public MetricDefinitionResponse GetMetricDefinition(MetricDefinitionRequest request)
        {
            MetricDefinitionResponse response = new MetricDefinitionResponse();

            try
            {
                response.MetricDefinition = this.metricsRepo.GetMetricDefinitionByField("Id", request.Id);
                response.Status = OperationStatus.Success;
            }
            catch (MetricsRepositoryException e)
            {
                response.Status = OperationStatus.Failure;
                response.Message = string.Format("Incomplete metric definition lookup for request {0} : {1}", request.Id, e.Message);
                response.Exception = e;
                _log.Error(response.Message, e);
            }

            return response;
        }
    }
}
